﻿using System;
using System.Collections.Generic;
using System.Text;
using Petra.Gis.DataServices;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;

namespace Petra.Gis.DataServices.SHP
{
  public partial class GeometryFactory : IGeometryFactory
  {
    //private pointers to functions to store in geometry mapping array ;-)
    private delegate GeometryBase CreateGeometryFn(byte[] geomBlob);
    private delegate byte[] SaveGeometryFn(GeometryBase geometry);

    private static Dictionary<ShapeGeometryTypes, CreateGeometryFn> storageToGeometry;
    private static Dictionary<System.Type, SaveGeometryFn> geometryToStorage;

    static GeometryFactory()
    {


      //setup storage to geometry functions
      storageToGeometry = new Dictionary<ShapeGeometryTypes, CreateGeometryFn>();
      storageToGeometry.Add(ShapeGeometryTypes.Point, new CreateGeometryFn(binaryToPoint));
      storageToGeometry.Add(ShapeGeometryTypes.PointM, new CreateGeometryFn(binaryToPoint));
      storageToGeometry.Add(ShapeGeometryTypes.PointZ, new CreateGeometryFn(binaryToPoint));
      storageToGeometry.Add(ShapeGeometryTypes.LineString, new CreateGeometryFn(binaryToLineString));
      storageToGeometry.Add(ShapeGeometryTypes.LineStringM, new CreateGeometryFn(binaryToLineString));
      storageToGeometry.Add(ShapeGeometryTypes.LineStringZ, new CreateGeometryFn(binaryToLineString));
      storageToGeometry.Add(ShapeGeometryTypes.Polygon, new CreateGeometryFn(binaryToPolygon));
      storageToGeometry.Add(ShapeGeometryTypes.PolygonM, new CreateGeometryFn(binaryToPolygon));
      storageToGeometry.Add(ShapeGeometryTypes.PolygonZ, new CreateGeometryFn(binaryToPolygon));
      //storageToGeometry.Add(ShapeGeometryTypes.PolygonZM, new CreateGeometryFn(binaryToPolygon));
      //storageToGeometry.Add(ShapeGeometryTypes.MultiPatch, new CreateGeometryFn(binaryToGeometryCollection));
      //storageToGeometry.Add(ShapeGeometryTypes.MultiPoint, new CreateGeometryFn(binaryToMultiPoint));
      //storageToGeometry.Add(ShapeGeometryTypes.MultiPointM, new CreateGeometryFn(binaryToMultiPoint));
      //storageToGeometry.Add(ShapeGeometryTypes.MultiPointZ, new CreateGeometryFn(binaryToMultiPoint));
      //storageToGeometry.Add(ShapeGeometryTypes.MultiPointZM, new CreateGeometryFn(binaryToMultiPoint));


      //setup geometry to storage functions
      geometryToStorage = new Dictionary<System.Type, SaveGeometryFn>();
      geometryToStorage.Add(typeof(Point), new SaveGeometryFn(pointToBinary));
      ////geometryToStorage.Add(typeof(Label), new SaveGeometryFn(labelToBinary));
      //geometryToStorage.Add(typeof(LineString), new SaveGeometryFn(linestringToBinary));
      //geometryToStorage.Add(typeof(Polygon), new SaveGeometryFn(polygonToBinary));
      //geometryToStorage.Add(typeof(Arc), new SaveGeometryFn(arcToBinary));
      //geometryToStorage.Add(typeof(LineRing), new SaveGeometryFn(LineRingToBinary));

      //geometryToStorage.Add(typeof(GeometryCollection), new SaveGeometryFn(geometryCollectionToBinary));
      //geometryToStorage.Add(typeof(MultiPoint), new SaveGeometryFn(geometryCollectionToBinary));
      //geometryToStorage.Add(typeof(MultiLabel), new SaveGeometryFn(geometryCollectionToBinary));
      //geometryToStorage.Add(typeof(MultiLine), new SaveGeometryFn(geometryCollectionToBinary));
      //geometryToStorage.Add(typeof(MultiArea), new SaveGeometryFn(geometryCollectionToBinary));

      //geometryToStorage.Add(typeof(Path), new SaveGeometryFn(pathToBinary));
      //geometryToStorage.Add(typeof(PathRing), new SaveGeometryFn(pathRingToBinary));
      //geometryToStorage.Add(typeof(Grid), new SaveGeometryFn(gridToBinary));
    }




    //-----------------------begin interface functions block--------------------------

    public GeometryBase CreateGeometry(object vData)
    {
      byte[] b = (byte[])vData;
      return createGeometry(b, _currGeomType);
    }





    //interface function saveGeometry
    public object SaveGeometry(GeometryBase oGeom)
    {
      return (object)saveGeometryBlob(oGeom);
    }





    //interface function canSave
    public bool CanSave(GeometryBase geometry)
    {
      //search Dictionary for delegate for this geometry
      try
      {
        SaveGeometryFn d = geometryToStorage[geometry.GetType()];
      }
      catch
      {
        return false;
      }
      return true;
    }
    //-----------------------end interface functions block--------------------------



    /*
     internal implementation of createGeometry
     if this function is called from inside of geometry (Boundary, Collection, CompositePolygon, CompositePolyline)
     arrayOffset must point to begin of subgeometry
     and parentGeomType defines relations, GDO has different definition then SFS, must perform check and conversion !!
    */
    private static GeometryBase createGeometry(byte[] vData, ShapeGeometryTypes type)
    {

      //call function in dictionary that will create geometry from byte[]
      return storageToGeometry[type](vData);

    }


    private static byte[] saveGeometryBlob(GeometryBase geometry)
    {
      return geometryToStorage[geometry.GetType()](geometry);
    }



    protected ShapeGeometryTypes _currGeomType;


    public ShapeGeometryTypes CurrentGeomType
    {
      get { return _currGeomType; }
      set { _currGeomType = value; }
    }


  }
}
